
package hasthi.metamodel;

import static hasthi.common.constants.HasthiConstants.LOGGER_NAME;
import hasthi.common.ChangeListener;
import hasthi.common.HasthiException;
import hasthi.common.InvaliedResourceDataException;
import hasthi.common.HasthiException.FaultCode;
import hasthi.manager.ManagedResourceType;
import hasthi.metamodel.resource.AbstractResourceModel;
import hasthi.metamodel.resource.Host;
import hasthi.metamodel.resource.EditableManagedResource;
import hasthi.metamodel.resource.ManagedLocalResourceImpl;
import hasthi.metamodel.resource.ManagedResource;
import hasthi.metamodel.resource.ManagedService;
import hasthi.metamodel.resource.ResourceModelContext;

import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.xml.namespace.QName;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * This resource model holds both local and remote objects. However only local resources can be 
 * added removed or edited. 
 * @author Srinath Perera(hperera@cs.indiana.edu)
 */


public class MixedResourceModel extends AbstractResourceModel{
    private static Log log = LogFactory.getLog(LOGGER_NAME);
    protected Map<QName,EditableManagedResource> localResourceRepository = new ConcurrentHashMap<QName, EditableManagedResource>();
    protected Map<QName,ManagedResource> remoteResourceRepository = new ConcurrentHashMap<QName, ManagedResource>();
    public MixedResourceModel(ResourceModelContext context) throws HasthiException {
        super(context);
    }

    public EditableManagedResource createNewResource(Map<QName, Object> extractedProperties,String managerAgentURL) throws InvaliedResourceDataException, HasthiException{
        EditableManagedResource resource = createNewResource(Placement.Local, extractedProperties, managerAgentURL);
        localResourceRepository.put(resource.getQName(), resource);
        return resource;
    }
    
    public EditableManagedResource createNewResource(ManagedResourceType resourceData) throws InvaliedResourceDataException, HasthiException{
        EditableManagedResource resource = createNewResource(Placement.Local, resourceData);
        localResourceRepository.put(resource.getQName(), resource);
        return resource;
    }

    
    public EditableManagedResource createNewResource(QName resourceName,String managerAgentURL,String category,String userDefinedType) throws InvaliedResourceDataException, HasthiException{
        EditableManagedResource resource = createNewResource(Placement.Local,resourceName,managerAgentURL,category,userDefinedType);
        localResourceRepository.put(resourceName, resource);
        return resource;
    }
    
    public EditableManagedResource removeResource(QName resourceName) throws HasthiException{
        if(localResourceRepository.containsKey(resourceName)){
            EditableManagedResource resource =  localResourceRepository.remove(resourceName);
            ChangeListener modelListener = context.getChangeListener();
            if(modelListener != null){
                modelListener.resourceRemoved(resource.getQName());    
            }
            return resource;
        }else if(remoteResourceRepository.containsKey(resourceName)){
            throw new HasthiException("remove resource is only defined for local copies "+ resourceName, FaultCode.InvaliedLocalArgumnet);
        }else{
            log.warn("Can not find resource "+resourceName + " to remove ");
        }
        return null;
    }
    
    public ManagedResource getResource(QName name) throws HasthiException{
        ManagedResource resource = localResourceRepository.get(name);
        if(resource != null){
            return resource;
        }else{
            ManagedResource remoteResource = remoteResourceRepository.get(name);
            if(remoteResource == null){
                String holderURL = ResourceUtils.fetchHolderURL(context,name);
                remoteResource = fetchRemoteResource(holderURL, name,false);
                if(remoteResourceRepository.size() > 20){
                    remoteResourceRepository.clear();
                }
                remoteResourceRepository.put(name, remoteResource);
            }
            return remoteResource;
        }
    }
    
    public EditableManagedResource getLocalResource(QName name){
        return localResourceRepository.get(name);
    }   
    
    /**
     * Only return local copies
     * @return
     */
    public Collection<QName> getResources(){
        return localResourceRepository.keySet();
    }
    
}

